import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


public class FallContainer {

	private List<BeanDefinition> beanDefinitionList = new ArrayList<BeanDefinition>();
	private Map<String, Object> beanMap = new HashMap<String, Object>();
	
	public void add(BeanDefinition beanDefinition) {
		beanDefinitionList.add(beanDefinition);
	} 
	
	public void initialize() {
		// initiate all of the beans first
		for (BeanDefinition def : beanDefinitionList) {
			try {
				// create an instance of the class
				Object bean = Class.forName(def.getClazz()).newInstance();
				beanMap.put(def.getName(), bean);
				System.out.println("Fall: creating bean, name=" + def.getName() + ", class=" + def.getClazz());
			} catch (Exception e) {
				e.printStackTrace();
				// convert to runtime exception
				throw new RuntimeException(e.getClass().getName() + " : " + e.getMessage());
			}
		}
		
		// set dependencies
		for (BeanDefinition def : beanDefinitionList) {
			Object bean = beanMap.get(def.getName());
			System.out.println("Fall: Looking for properties (dependencies) for bean " + def.getName());
			for (BeanProperty property : def.getPropertyList()) {
				// get the dependency
				Object dependency = beanMap.get(property.getReference());
				if (dependency == null) {
					throw new RuntimeException("Attempting to set property on " + def.getName() + ", no bean found with name " + property.getName());
				}
				try {
					// call the specified method
					Method method = getMethodWithName(bean.getClass(), property.getName());
					Object arguments[] = new Object[1];
					arguments[0] = dependency;
		            method.invoke(bean, arguments);
		            System.out.println("   called " + property.getName() + "(" + property.getReference() + ")");
				} catch (Exception e) {
					e.printStackTrace();
					// convert to runtime exception
					throw new RuntimeException(e.getClass().getName() + " : " + e.getMessage());
				}
			}
		}
	}
	
	private Method getMethodWithName(Class clazz, String name) {
		for (Method method : clazz.getMethods()) {
			if (method.getName().equals(name)) {
				return method;
			}
		}
		throw new RuntimeException("No method named " + name + " was found for class " + clazz.getCanonicalName());
	}
	
	public Object getBean(String name) {
		return beanMap.get(name);
	}
}
